home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / utils / jpegdump / jpegdump.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-26  |  22.9 KB  |  867 lines

  1. /*
  2.    jpegdump.c - dump JPEG marker information
  3.  
  4.    v1.00  1992.06.09  initial release
  5.    v1.01  1992.06.22  non-JFIF APP0 markers didn't display first five bytes
  6.    v1.02  1992.07.20  eliminated fseeks which seem to be broken, even for
  7.                       forward seeks only, on pipes on some systems
  8.    v1.03  1992.08.24  added offset and stop at eoi options
  9.    v1.04  1992.08.24  added parsing of JFIF extension markers as per JFIF
  10.                       v1.02 draft of 1992.08.21
  11.    v1.05  1992.08.25  added -r to recurse into JPEG compressed thumbnails
  12.                       (completely untested; probably doesn't work)
  13.    v1.06  1992.09.14  incorporated Phil Richards' code to guess cjpeg quality
  14.                       factor
  15.    v1.07  1992.09.15  added extension 0x13 per final JFIF 1.02; repaired bugs
  16.                       in thumbnail-related code
  17.    v1.08  1992.12.23  Provide multiple levels of verbosity; miscellaneous
  18.                       cleanups
  19.    v1.09  1993.03.01  Add switch for hex/char printout of COM,APPn data
  20.    v1.10  1993.10.26  Identify -q 100 quant tables correctly; more tweaking
  21.               of verbosity levels
  22.  
  23. Copyright (c) 1992 Handmade Software, Inc.
  24. by Allan N. Hessenflow
  25.  
  26. Permission to use, copy, modify, and distribute this software and
  27. its documentation for any purpose and without fee is hereby granted,
  28. provided that the above copyright notice appear in all copies and
  29. that both that copyright notice and this permission notice appear in
  30. supporting documentation.  This software is provided "as is" without
  31. express or implied warranty.
  32.  
  33.  
  34. Usage:
  35.   jpegdump [-help] [-q] [-v] [-o n] [-c] [-noc] [-r] [-nor] [-hex] [-nohex]
  36.            [file1 [file2...]]
  37.  
  38.   -q       quiet: minimal information printed
  39.   -v       verbose: more information printed (multiple -v's give more output)
  40.   -o n     skip first n bytes of file (1234 is decimal, 0x1234 for hex)
  41.   -c       continue past EOI to actual end of file (default)
  42.   -noc     stop upon seeing EOI marker
  43.   -r       recurse into jpeg compressed thumbnails (use at your own risk)
  44.   -nor     don't recurse into thumbnails (default)
  45.   -hex     show COM and APPn data in hex
  46.   -nohex   show COM and APPn data in ASCII when possible (default)
  47.  
  48. The numbers printed in the `Approximate quality factor' line are as follows:
  49.   Quality: an estimate of the quality factor used when cjpeg was run.
  50.   Scaling factor (scale): mean ratio between quantization table entries
  51.     and JPEG sample table entries, times 100.
  52.   Variance (var): squared standard deviation of the above ratio.
  53.     If this is larger than about 2, then the table is not a simple
  54.     multiple of the standard's sample table, so the file was NOT
  55.     generated by cjpeg and the quality estimate is dubious.
  56.  
  57. Bugs:
  58.   garbage in, garbage out
  59.  
  60.  
  61. Build instructions:
  62.      MSC v7.00 (substitute the path to setargv.obj for \c700\lib\):
  63.        cl jpegdump.c \c700\lib\setargv.obj /link /NOE
  64.      Watcom C/386 v9.0 (substitute the path to wildargv.c for \watcom\src\starup\)
  65.        wcl386 jpegdump.c \watcom\src\startup\wildargv.c
  66.      acc:
  67.        acc jpegdump.c -o jpegdump
  68.      gcc:
  69.        gcc jpegdump.c -o jpegdump
  70.      others:
  71.        any ANSI C compiler should work.  If you don't define CASESENSITIVE
  72.        then you may need to provide your own strnicmp on some systems; this
  73.        is just a case insensitive version of strncmp.  If you're too lazy
  74.        to do that, just define CASESENSITIVE and be sure to enter the
  75.        switches in lower case.
  76.  
  77.  
  78. Bug reports, comments to:
  79.      allanh@netcom.com
  80. */
  81.  
  82. #include <stdio.h>
  83. #include <stdlib.h>
  84. #include <string.h>
  85. #include <ctype.h>
  86. #ifdef MSDOS
  87. #include <fcntl.h>        /* for setmode() call */
  88. #endif
  89.  
  90. #ifdef CASESENSITIVE        /* set to disable use of strnicmp */
  91. #define strnicmp(a,b,len)    strncmp(a,b,len)
  92. #endif
  93.  
  94.  
  95. /* JPEG marker codes */
  96. #define TEM  0x01
  97. #define SOF  0xc0
  98. #define DHT  0xc4
  99. #define JPGA 0xc8
  100. #define DAC  0xcc
  101. #define RST  0xd0
  102. #define SOI  0xd8
  103. #define EOI  0xd9
  104. #define SOS  0xda
  105. #define DQT  0xdb
  106. #define DNL  0xdc
  107. #define DRI  0xdd
  108. #define DHP  0xde
  109. #define EXP  0xdf
  110. #define APP  0xe0
  111. #define JPG  0xf0
  112. #define COM  0xfe
  113.  
  114. /* Sample quantization tables from JPEG spec --- only needed for
  115.  * guesstimate of quality factor
  116.  */
  117. static int std_luminance_quant_tbl[64] = {
  118.   16,  11,  12,  14,  12,  10,  16,  14,
  119.   13,  14,  18,  17,  16,  19,  24,  40,
  120.   26,  24,  22,  22,  24,  49,  35,  37,
  121.   29,  40,  58,  51,  61,  60,  57,  51,
  122.   56,  55,  64,  72,  92,  78,  64,  68,
  123.   87,  69,  55,  56,  80, 109,  81,  87,
  124.   95,  98, 103, 104, 103,  62,  77, 113,
  125.  121, 112, 100, 120,  92, 101, 103,  99
  126. };
  127.  
  128. static int std_chrominance_quant_tbl[64] = {
  129.   17,  18,  18,  24,  21,  24,  47,  26,
  130.   26,  47,  99,  66,  56,  66,  99,  99,
  131.   99,  99,  99,  99,  99,  99,  99,  99,
  132.   99,  99,  99,  99,  99,  99,  99,  99,
  133.   99,  99,  99,  99,  99,  99,  99,  99,
  134.   99,  99,  99,  99,  99,  99,  99,  99,
  135.   99,  99,  99,  99,  99,  99,  99,  99,
  136.   99,  99,  99,  99,  99,  99,  99,  99
  137. };
  138.  
  139. static int *deftabs[2] =
  140.     { std_luminance_quant_tbl, std_chrominance_quant_tbl };
  141.  
  142.  
  143. typedef enum {false, true} boolean;
  144.  
  145. /* switch table */
  146. static struct opt {
  147.   char *string;
  148.   enum { helpO, quietO, verboseO, offsetO, continueO, noContinueO,
  149.      recurseO, noRecurseO, hexcharsO, nohexcharsO } id;
  150. } options[] = {
  151.   {"?", helpO},
  152.   {"help", helpO},
  153.   {"quiet", quietO},
  154.   {"verbose", verboseO},
  155.   {"offset", offsetO},
  156.   {"continueaftereoi", continueO},
  157.   {"nocontinueaftereoi", noContinueO},
  158.   {"recurseintojpegthumbnails", recurseO},
  159.   {"norecurseintojpegthumbnails", noRecurseO},
  160.   {"hex", hexcharsO},
  161.   {"nohex", nohexcharsO}
  162. };
  163.  
  164. static boolean hexchars;    /* state of hex-chars switch */
  165.  
  166.  
  167. static long dumpMarkers(FILE *, char *, int, boolean, boolean, long);
  168. static unsigned int getWordMoto(FILE *);
  169.  
  170.  
  171. int
  172. main (int argc, char **argv)
  173. {
  174.   FILE *inFile;
  175.   int numFileNames;
  176.   int i;
  177.   int verbosity;
  178.   boolean skipeoi, recurse;
  179.   long offset, j;
  180.   
  181.   /* default switch settings */
  182.   offset=0L;
  183.   skipeoi=true;
  184.   recurse=false;
  185.   verbosity=0;
  186.   hexchars=false;
  187.   
  188.   /* skip invocation name */
  189.   argc--;
  190.   argv++;
  191.   
  192.   /* scan arguments */
  193.   numFileNames=0;
  194.  
  195.   while (argc) {
  196.     if (**argv=='-'
  197. #ifdef MSDOS
  198.     || **argv=='/'
  199. #endif
  200.     ) {
  201.       /* Process a switch */
  202.       size_t length;
  203.       int selectedOption;
  204.       boolean multipleMatches;
  205.       
  206.       length=strlen((*argv)+1);
  207.       selectedOption=-1;
  208.       multipleMatches=false;
  209.       for (i=0; i<sizeof(options)/sizeof(struct opt); i++) {
  210.     if (strnicmp((*argv)+1, options[i].string, length)==0) {
  211.       if (selectedOption>=0)
  212.         if (options[i].id!=options[selectedOption].id)
  213.           multipleMatches=true;
  214.       selectedOption=i;
  215.       if (length==strlen(options[i].string)) {
  216.         multipleMatches=false;
  217.         break;
  218.       }
  219.     }
  220.       }
  221.       if (selectedOption>=0 && !multipleMatches) {
  222.     switch(options[selectedOption].id) {
  223.     case helpO:
  224.       fprintf(stderr,
  225.           "usage: jpegdump [-help] [-q] [-v] [-o n] [-c] [-noc] [-r] [-nor] [-hex] [-nohex] [file1 [file2...]]\n");
  226.       exit(0);
  227.       break;
  228.     case quietO:
  229.       verbosity--;
  230.       break;
  231.     case verboseO:
  232.       verbosity++;
  233.       break;
  234.     case offsetO:
  235.       if (argc>1) {
  236.         argc--;
  237.         argv++;
  238.         offset=atol(*argv);
  239.       }
  240.       break;
  241.     case continueO:
  242.       skipeoi=true;
  243.       break;
  244.     case noContinueO:
  245.       skipeoi=false;
  246.       break;
  247.     case recurseO:
  248.       recurse=true;
  249.       break;
  250.     case noRecurseO:
  251.       recurse=false;
  252.       break;
  253.     case hexcharsO:
  254.       hexchars=true;
  255.       break;
  256.     case nohexcharsO:
  257.       hexchars=false;
  258.       break;
  259.     }
  260.       } else if (multipleMatches) {
  261.     fprintf(stderr, "ambiguous option %s\n", *argv);
  262.     exit(-1);
  263.       } else {
  264.     fprintf(stderr, "unrecognized option %s\n", *argv);
  265.     exit(-1);
  266.       }
  267.     } else {
  268.       /* not a switch, process input file with current switch settings */
  269.       numFileNames++;
  270.       inFile=fopen(*argv, "rb");
  271.       if (inFile==NULL) {
  272.     fprintf(stderr, "can't open file %s\n", *argv);
  273.       } else {
  274.     printf("\n%s:\n", *argv);
  275.     for (j=0L; j<offset; j++) /* skip specified offset */
  276.       getc(inFile);
  277.     dumpMarkers(inFile, "", verbosity, skipeoi, recurse, offset);
  278.     fclose(inFile);
  279.       }
  280.     }
  281.     argv++;
  282.     argc--;
  283.   }
  284.  
  285.   /* Process standard input if no file names were found */
  286.   if (numFileNames==0) {
  287.     inFile=stdin;
  288. #ifdef MSDOS
  289.     /* turn off newline translation */
  290.     setmode(fileno(inFile), O_BINARY);
  291. #endif
  292.     for (j=0L; j<offset; j++)    /* skip specified offset */
  293.       getc(inFile);
  294.     dumpMarkers(inFile, "", verbosity, skipeoi, recurse, offset);
  295.   }
  296.  
  297.   return 0;
  298. }
  299.  
  300.  
  301. static void
  302. printChar (int c)
  303. {
  304.   if (hexchars) {
  305.     printf("$%02x ", c);
  306.   } else {
  307.     if (c == '\\')
  308.       printf("\\\\");
  309.     else if (isprint(c) || c == '\n')
  310.       putchar(c);
  311.     else
  312.       printf("\\%03o", c);
  313.   }
  314. }
  315.  
  316.  
  317. static long
  318. dumpMarkers (FILE *stream, char *prefix, int verbosity,
  319.          boolean continueAfterEOI, boolean recurseIntoThumbnails,
  320.          long startingOffset)
  321. {
  322.   int marker;
  323.   int c, c2, c3, c4, c5, row, col, i;
  324.   unsigned int height, aword;
  325.   long length;
  326.   unsigned char huff[16];
  327.   long totalBytesRead;
  328.   char *str;
  329.   char *newPrefix;
  330.   
  331.   totalBytesRead=0L;
  332.  
  333.   while (1) {
  334.     c=getc(stream);
  335.     if (c==EOF)
  336.       break;
  337.     totalBytesRead++;
  338.     /* marker prefix? */
  339.     if (c!=0xff)
  340.       continue;
  341.     /* get marker code, skipping fill bytes */
  342.     do {
  343.       c=getc(stream);
  344.       totalBytesRead++;
  345.     } while (c==0xff);
  346.     if (c==EOF)
  347.       break;
  348.     /* ignore stuffed FF/00 sequences */
  349.     if (c==0)
  350.       continue;
  351.     /* OK, we've apparently found a marker */
  352.     marker=c;
  353.     /* Minimum verbosity to print marker is 2 for RSTs, 0 for rest */
  354.     if (verbosity>0 && (marker<RST+0 || marker>RST+7 || verbosity>2))
  355.       printf("%soffset $%lx ", prefix, totalBytesRead+startingOffset-2L);
  356.     length=0L;
  357.     switch (marker) {
  358.     case SOI:
  359.       if (verbosity>0)
  360.     printf("SOI\n");
  361.       break;
  362.     case DRI:
  363.       length=(long) getWordMoto(stream);
  364.       aword = getWordMoto(stream);
  365.       if (verbosity>0) {
  366.     printf("DRI (length %ld)\n", length);
  367.     printf("%s  restart interval %u MCUs\n", prefix, aword);
  368.       }
  369.       totalBytesRead+=4L;
  370.       length-=4L;
  371.       break;
  372.     case APP+0:
  373.     case APP+1:
  374.     case APP+2:
  375.     case APP+3:
  376.     case APP+4:
  377.     case APP+5:
  378.     case APP+6:
  379.     case APP+7:
  380.     case APP+8:
  381.     case APP+9:
  382.     case APP+10:
  383.     case APP+11:
  384.     case APP+12:
  385.     case APP+13:
  386.     case APP+14:
  387.     case APP+15:
  388.       length=(long) getWordMoto(stream);
  389.       if (verbosity>0)
  390.     printf("APP%d (length %ld)\n", marker-APP, length);
  391.       totalBytesRead+=2L;
  392.       length-=2L;
  393.  
  394.       if (marker==APP+0 && length>=14L) {
  395.     char signature[5];
  396.     
  397.     fread(signature, 1, 5, stream);
  398.     totalBytesRead+=5L;
  399.     length-=5L;
  400.     if (memcmp(signature, "JFIF", 5)==0) {
  401.       unsigned int dpiX, dpiY, thumbX, thumbY;
  402.       long thumbBytes;
  403.       
  404.       aword = getWordMoto(stream);
  405.       c=getc(stream);
  406.       dpiX=getWordMoto(stream);
  407.       dpiY=getWordMoto(stream);
  408.       thumbX=(unsigned int) getc(stream);
  409.       thumbY=(unsigned int) getc(stream);
  410.       totalBytesRead+=9L;
  411.       length-=9L;
  412.       if (verbosity>0) {
  413.         printf("%s  JFIF version %04x, ", prefix, aword);
  414.         switch (c) {
  415.         case 0:
  416.           printf("aspect ratio %u:%u\n", dpiX, dpiY);
  417.           break;
  418.         case 1:
  419.           printf("%u x %u dpi\n", dpiX, dpiY);
  420.           break;
  421.         case 2:
  422.           printf("%u x %u dpcm\n", dpiX, dpiY);
  423.           break;
  424.         default:
  425.           printf("bogus aspect ratio code %d %u %u\n", c, dpiX, dpiY);
  426.           break;
  427.         }
  428.         printf("%s  thumbnail size %u x %u\n", prefix, thumbX, thumbY);
  429.       }
  430.       thumbBytes = (long)thumbX*(long)thumbY*3L;
  431.       length-=thumbBytes;
  432.       totalBytesRead+=thumbBytes;
  433.       while (thumbBytes--)
  434.         getc(stream);
  435.     } else if (memcmp(signature, "JFXX", 5)==0) {
  436.       int extension;
  437.       unsigned int thumbX, thumbY;
  438.       long thumbBytes;
  439.       
  440.       extension=getc(stream);
  441.       totalBytesRead++;
  442.       length--;
  443.       switch (extension) {
  444.       case 0x10:
  445.         if (verbosity>0)
  446.           printf("%s  JFIF extension JPEG thumbnail\n", prefix);
  447.         if (recurseIntoThumbnails) {
  448.           long bytes;
  449.           
  450.           newPrefix=(char *) malloc(strlen(prefix)+5);
  451.           if (newPrefix==NULL) {
  452.         fprintf(stderr, "out of memory\n");
  453.         exit(-1);
  454.           }
  455.           strcpy(newPrefix, prefix);
  456.           strcat(newPrefix, "    ");
  457.           bytes=dumpMarkers(stream, newPrefix, verbosity-1,
  458.                 false, true, startingOffset+totalBytesRead);
  459.           totalBytesRead+=bytes;
  460.           length-=bytes;
  461.           free(newPrefix);
  462.         } else {
  463.           while (length>0L) {
  464.         getc(stream);
  465.         totalBytesRead++;
  466.         length--;
  467.           }
  468.         }
  469.         break;
  470.       case 0x11:
  471.         if (verbosity>0)
  472.           printf("%s  JFIF extension 1 byte/pixel thumbnail\n", prefix);
  473.         thumbX=(unsigned int) getc(stream);
  474.         thumbY=(unsigned int) getc(stream);
  475.         totalBytesRead+=2L;
  476.         length-=2L;
  477.         if (verbosity>0)
  478.           printf("%s    thumbnail size %u x %u\n", prefix, thumbX, thumbY);
  479.         if (verbosity>2)
  480.           printf("%s    palette:\n", prefix);
  481.         for (i=0; i<256; i++) {
  482.           int red, green, blue;
  483.           red=getc(stream);
  484.           green=getc(stream);
  485.           blue=getc(stream);
  486.           totalBytesRead+=3L;
  487.           length-=3L;
  488.           if (verbosity>2)
  489.         printf("%s      %3d %3d %3d\n", prefix, red, green, blue);
  490.         }
  491.         thumbBytes = (long)thumbX*(long)thumbY;
  492.         length-=thumbBytes;
  493.         totalBytesRead+=thumbBytes;
  494.         while (thumbBytes--)
  495.           getc(stream);
  496.         break;
  497.       case 0x13:
  498.         if (verbosity>0)
  499.           printf("%s  JFIF extension 3 byte/pixel thumbnail\n", prefix);
  500.         thumbX=(unsigned int) getc(stream);
  501.         thumbY=(unsigned int) getc(stream);
  502.         totalBytesRead+=2L;
  503.         length-=2L;
  504.         if (verbosity>0)
  505.           printf("%s    thumbnail size %u x %u\n", prefix, thumbX, thumbY);
  506.         thumbBytes = (long)thumbX*(long)thumbY*3L;
  507.         length-=thumbBytes;
  508.         totalBytesRead+=thumbBytes;
  509.         while (thumbBytes--)
  510.           getc(stream);
  511.         break;
  512.       default:
  513.         if (verbosity>0)
  514.           printf("%s  JFIF extension $%02x\n", prefix, extension);
  515.         break;
  516.       }
  517.     } else {
  518.       /* Unrecognized APP0 marker */
  519.       if (verbosity>0) {
  520.         printf("%s  ", prefix);
  521.         for (i=0; i<5; i++)
  522.           printChar((int) signature[i]);
  523.       }
  524.       while (length>0L) {
  525.         c = getc(stream);
  526.         if (verbosity>0)
  527.           printChar(c);
  528.         totalBytesRead++;
  529.         length--;
  530.       }
  531.       if (verbosity>0)
  532.         printf("\n");
  533.     }
  534.       }
  535.       /* Print any remaining data in the APP marker */
  536.       if (length) {
  537.     if (verbosity>0)
  538.       printf("%s  ", prefix);
  539.     while (length>0L) {
  540.       c = getc(stream);
  541.       if (verbosity>0)
  542.         printChar(c);
  543.       totalBytesRead++;
  544.       length--;
  545.     }
  546.     if (verbosity>0)
  547.       printf("\n");
  548.       }
  549.       break;
  550.     case COM:
  551.       length=(long) getWordMoto(stream);
  552.       if (verbosity>0)
  553.     printf("COM (length %ld)\n  ", length);
  554.       totalBytesRead+=2L;
  555.       length-=2L;
  556.       while (length>0L) {
  557.     c = getc(stream);
  558.     if (verbosity>0)
  559.       printChar(c);
  560.     totalBytesRead++;
  561.     length--;
  562.       }
  563.       if (verbosity>0)
  564.     printf("\n");
  565.       break;
  566.     /* the following all have the same syntax and fall through to one parser */
  567.     case SOF+0:
  568.       str="SOF0 (baseline DCT Huffman)";
  569.       goto frame;
  570.     case SOF+1:
  571.       str="SOF1 (extended sequential DCT Huffman)";
  572.       goto frame;
  573.     case SOF+2:
  574.       str="SOF2 (progressive DCT Huffman)";
  575.       goto frame;
  576.     case SOF+3:
  577.       str="SOF3 (spatial lossless Huffman)";
  578.       goto frame;
  579.     case SOF+9:
  580.       str="SOF9 (extended sequential DCT arithmetic)";
  581.       goto frame;
  582.     case SOF+10:
  583.       str="SOF10 (progressive DCT arithmetic)";
  584.       goto frame;
  585.     case SOF+11:
  586.       str="SOF11 (spatial lossless arithmetic)";
  587.       goto frame;
  588.       /* the following SOF markers are for differential coding;
  589.      they are listed on page B-2 of CD 10918-1, but for some
  590.      reason are not listed on B-6 and B-7, where the SOF syntax
  591.      is given.  This suggests that the syntax may be different
  592.      for these markers, but it doesn't seem to be defined
  593.      anywhere else in the document.  */
  594.     case SOF+5:
  595.       str="SOF5 (differential sequential DCT Huffman)";
  596.       goto frame;
  597.     case SOF+6:
  598.       str="SOF6 (differential progressive DCT Huffman)";
  599.       goto frame;
  600.     case SOF+7:
  601.       str="SOF7 (differential spatial Huffman)";
  602.       goto frame;
  603.     case SOF+13:
  604.       str="SOF13 (differential sequential DCT arithmetic)";
  605.       goto frame;
  606.     case SOF+14:
  607.       str="SOF14 (differential progressive DCT arithmetic)";
  608.       goto frame;
  609.     case SOF+15:
  610.       str="SOF15 (differential spatial arithmetic)";
  611.       goto frame;
  612.     case DHP:
  613.       str="DHP";
  614.       
  615.     frame:
  616.       length=(long) getWordMoto(stream);
  617.       if (verbosity>0)
  618.     printf("%s (length %ld)\n", str, length);
  619.       totalBytesRead+=2L;
  620.       length-=2L;
  621.       c=getc(stream);
  622.       height=getWordMoto(stream);
  623.       aword=getWordMoto(stream);
  624.       c2=getc(stream);
  625.       totalBytesRead+=6L;
  626.       length-=6L;
  627.       if (verbosity>0)
  628.     printf("%s  sample precision %d\n", prefix, c);
  629.       if (verbosity>= -1)      /* even very terse output includes dimensions */
  630.     printf("%s  width %u, height %u  components %d\n", prefix,
  631.            aword, height, c2);
  632.       while (c2--) {
  633.     c3=getc(stream);
  634.     c4=getc(stream);
  635.     c5=getc(stream);
  636.     totalBytesRead+=3L;
  637.     length-=3L;
  638.     if (verbosity>=0)    /* terse output includes sampling factors */
  639.       printf("%s    id %d horizontal sampling %d, vertical sampling %d, quantization table %d\n", prefix, c3, c4>>4, c4&0x0f, c5);
  640.       }
  641.       break;
  642.     case SOS:
  643.       length=(long) getWordMoto(stream);
  644.       if (verbosity>0)
  645.     printf("SOS (length %ld)\n", length);
  646.       totalBytesRead+=2L;
  647.       length-=2L;
  648.       c2=getc(stream);
  649.       totalBytesRead++;
  650.       length--;
  651.       if (verbosity>0)
  652.     printf("%s  components %d\n", prefix, c2);
  653.       while (c2--) {
  654.     c3=getc(stream);
  655.     c4=getc(stream);
  656.     totalBytesRead+=2L;
  657.     length-=2L;
  658.     if (verbosity>0)
  659.       printf("%s    id %d dc table %d, ac table %d\n", prefix,
  660.          c3, c4>>4, c4&0x0f);
  661.       }
  662.       c=getc(stream);
  663.       c2=getc(stream);
  664.       c3=getc(stream);
  665.       totalBytesRead+=3L;
  666.       length-=3L;
  667.       if (verbosity>0)
  668.     printf("%s  spectral selection %d to %d, bit position high %d, low %d\n",
  669.            prefix, c, c2, c3>>4, c3&0x0f);
  670.       break;
  671.     case DQT:
  672.       length=(long) getWordMoto(stream);
  673.       if (verbosity>0)
  674.     printf("DQT (length %ld)\n", length);
  675.       totalBytesRead+=2L;
  676.       length-=2L;
  677.  
  678.       while (length>0L) {
  679.     int tableindex;
  680.     int *table = NULL;
  681.     double cumsf = 0.0, cumsf2 = 0.0;
  682.     int allones = 1;
  683.     
  684.     c=getc(stream);
  685.     totalBytesRead++;
  686.     length--;
  687.     tableindex = c & 0x0f;
  688.     if (verbosity>0)
  689.       printf("%s  table %d precision %d\n", prefix, tableindex,
  690.          (c>>4) ? 16 : 8);
  691.     if (tableindex < 2)
  692.       table = deftabs[tableindex];
  693.  
  694.     for (row=0; row<8; row++) {
  695.       if (verbosity>1)
  696.         printf("%s    ", prefix);
  697.       for (col=0; col<8; col++) {
  698.         unsigned int val;
  699.         
  700.         if (c>>4) {
  701.           val=getWordMoto(stream);
  702.           totalBytesRead+=2L;
  703.           length-=2L;
  704.         } else {
  705.           val=(unsigned int) getc(stream);
  706.           totalBytesRead++;
  707.           length--;
  708.         }
  709.         if (verbosity>1)
  710.           printf("%5u ", val);
  711.         if (table) {
  712.           double x;
  713.           /* scaling factor in percent */
  714.           x = 100.0 * (double)val / (double)table[row*8+col];
  715.           cumsf += x;
  716.           cumsf2 += x * x;
  717.           /* separate check for all-ones table (Q 100) */
  718.           if (val != 1) allones = 0;
  719.         }
  720.       }
  721.       if (verbosity>1)
  722.         printf("\n");
  723.     }
  724.     if (table) {
  725.       double qual, var;
  726.       
  727.       cumsf /= 64.0;    /* mean scale factor */
  728.       cumsf2 /= 64.0;
  729.       var = cumsf2 - (cumsf * cumsf); /* variance */
  730.       if (allones)        /* special case for all-ones table */
  731.         qual = 100.0;
  732.       else if (cumsf <= 100.0)
  733.         qual = (200.0 - cumsf) / 2.0;
  734.       else
  735.         qual = 5000.0 / cumsf;
  736.       if (verbosity>=0)    /* terse output includes quality */
  737.         printf("%s  Approximate quality factor for qtable %d: %.0f (scale %.2f, var %.2f)\n", prefix,
  738.            tableindex, qual, cumsf, var);
  739.     }
  740.       }
  741.       break;
  742.     case DHT:
  743.       length=(long) getWordMoto(stream);
  744.       if (verbosity>0)
  745.     printf("DHT (length %ld)\n", length);
  746.       totalBytesRead+=2L;
  747.       length-=2L;
  748.       while (length>0L) {
  749.     c=getc(stream);
  750.     totalBytesRead++;
  751.     length--;
  752.     if (verbosity>0)
  753.       printf("%s  table %d\n", prefix, c);
  754.     for (i=0; i<16; i++) {
  755.       huff[i]=(unsigned char) getc(stream);
  756.     }
  757.     totalBytesRead+=16L;
  758.     length-=16L;
  759.     for (i=0; i<16; i++) {
  760.       if (verbosity>1)
  761.         printf("%s    bits %2d (codes=%3u) ", prefix, i+1,
  762.            (unsigned int) huff[i]);
  763.       while (huff[i]--) {
  764.         c2 = getc(stream);
  765.         totalBytesRead++;
  766.         length--;
  767.         if (verbosity>1)
  768.           printf("$%02x ", c2);
  769.       }
  770.       if (verbosity>1)
  771.         printf("\n");
  772.     }
  773.       }
  774.       break;
  775.     case DAC:
  776.       length=(long) getWordMoto(stream);
  777.       if (verbosity>0)
  778.     printf("DAT (length %ld)\n", length);
  779.       totalBytesRead+=2L;
  780.       length-=2L;
  781.       while (length>0L) {
  782.     c=getc(stream);
  783.     c2=getc(stream);
  784.     totalBytesRead+=2L;
  785.     length-=2L;
  786.     if (verbosity>0)
  787.       printf("%s  id %d conditioning %d\n", prefix, c, c2);
  788.       }
  789.       break;
  790.     case RST+0:
  791.     case RST+1:
  792.     case RST+2:
  793.     case RST+3:
  794.     case RST+4:
  795.     case RST+5:
  796.     case RST+6:
  797.     case RST+7:
  798.       if (verbosity>2)
  799.     printf("RST%d\n", marker-RST);
  800.       break;
  801.     case DNL:
  802.       length=(long) getWordMoto(stream);
  803.       aword = getWordMoto(stream);
  804.       totalBytesRead+=4L;
  805.       length-=4L;
  806.       if (verbosity>0) {
  807.     printf("DNL (length %ld)\n", length);
  808.     printf("%s  lines %u\n", prefix, aword);
  809.       }
  810.       break;
  811.     case EOI:
  812.       if (verbosity>0)
  813.     printf("EOI\n");
  814.       break;
  815.     case EXP:
  816.       length=(long) getWordMoto(stream);
  817.       if (verbosity>0)
  818.     printf("DHP (length %ld)\n", length);
  819.       c=getc(stream);
  820.       if (verbosity>0)
  821.     printf("%s  horizontal expansion %d, vertical expansion %d\n", prefix,
  822.            c>>4, c&0x0f);
  823.       totalBytesRead+=3L;
  824.       length-=3L;
  825.       break;
  826.     case TEM:
  827.       if (verbosity>0)
  828.     printf("TEM\n");
  829.       break;
  830.     default:
  831.       length=(long) getWordMoto(stream);
  832.       if (verbosity>0)
  833.     printf("marker $%02x (length %ld)\n", c, length);
  834.       totalBytesRead+=2L;
  835.       length-=2L;
  836.       if (verbosity>0)
  837.     printf("%s  ", prefix);
  838.       while (length>0L) {
  839.     c=getc(stream);
  840.     totalBytesRead++;
  841.     length--;
  842.     if (verbosity>0)
  843.       printChar(c);
  844.       }
  845.       if (verbosity>0)
  846.     printf("\n");
  847.       break;
  848.     } /* end switch (marker) */
  849.     if (length && verbosity>0)
  850.       printf("%s  bad length (residual=%ld)\n", prefix, length);
  851.     if (marker==EOI && !continueAfterEOI)
  852.       break;
  853.   } /* end while */
  854.  
  855.   return totalBytesRead;
  856. }
  857.  
  858.  
  859. static unsigned int
  860. getWordMoto (FILE *stream)
  861. {
  862.   register unsigned int temp;
  863.  
  864.   temp=(unsigned int) (getc(stream)<<8);
  865.   return (unsigned int) getc(stream) | temp;
  866. }
  867.